<style lang="scss" scoped>
    .container{
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
    }
    .container ul{
        margin: 0;
        padding: 0;
    }
    .container ul li.shadow{
        border: 3px dashed #ddd;
        background: white;
        z-index: 50;
        display: none;
    }
    .container ul li{
        position: absolute;
        background: white;
        list-style: none;
        margin: 10px;
        z-index: 100;
        display: none;
    }
    li.animation{
        transition: transform .25s,width .25s,height .25s;
    }
    .item.changing{
        z-index: 500;
    }
    .item .resizeHandle{
        width: 15px;
        height: 15px;
        border-bottom: solid 2px black;
        border-right: 2px solid black;
        position: absolute;
        bottom: 5px;
        right: 5px;
        cursor: se-resize;
        display: none;
        z-index: 9999
    }
    .item .close{
      position: absolute;
      top: 10px;
      right: 7px;
      display: none;
    }
    .item:hover .close{
      display: block;
      cursor: pointer;
    }
    .item:hover .resizeHandle{
      display: block;
    }
</style>

<template>
    <div class="container">
        <ul>
            <li v-for="value in boardData" class="item animation" :item-name="value.name">
                <chart-topo v-if="value.type == 'topo'"
                  :tooltip="false"
                  :draggable="false"
                  :isAutoHeight="true"
                  :iconSize="30"
                  :data="value.data"
                  :loading="value.loading"
                  :title="value.desc">
                </chart-topo>
                <cm-line v-if="value.type == 'line'"
                  :isAutoHeight="true"
                  :data="value.data"
                  :title="value.desc"
                  :loading="value.loading"
                ></cm-line>
                <i class="ion-close-round close"></i>
                <div class="resizeHandle"></div>
            </li>
            <li class="shadow" item-name="shadow"></li>
        </ul>
    </div>
</template>

<script>
import ChartTopo from '_charts/chart-topo'
export default{
  name: 'dashboard',
  components: { 
    ChartTopo,
    'cm-line': require('_charts/chart-lines')
  },
  data () {
    return {
      dragFlag: false,
      startX: 0,
      startY: 0,
      dragDom: null,
      dragData: null,
      resizeFlag: false,
      resizeData: null,
      dragShadow: true,
      currentGrid: {},
      boards: [],
      maxRow: 0,
      maxCol: 0
    }
  },
  props: {
    /*
     * 加载数据标识
     */
    cell: {
      type: Number,
      default: 100
    },
    space: {
      type: Number,
      default: 10
    },
    boardData: {
      type: Object,
      default: {}
    }
  },

  methods: {
    createShadow () {
      var $shadow = $('.container ul li.shadow')
      $shadow.width($(this.dragDom).width())
      $shadow.height($(this.dragDom).height())
      $shadow.css('transform', $(this.dragDom)[0].style.transform)
      $shadow.addClass('animation')
      if (this.dragData) {
        this.boardData.shadow = this.copyGrid(this.dragData)
        this.boardData.shadow.name = 'shadow'
      }
      $shadow.show()
    },
    removeShadow () {
      var $shadow = $('.container ul li.shadow')
      $(this.dragDom).css('transform', $shadow[0].style.transform)
      $shadow.hide()
      delete this.boardData.shadow
    },
    moveShadow (row, col, directorY) {
      var boardData = this.boardData
      var boards = this.boards
      if ((directorY > 0 && row < boardData.shadow.row) || (directorY < 0 && row > boardData.shadow.row)) {
        return {'row': boardData.shadow.row, 'col': boardData.shadow.col}
      }
      // 若向下移动
      if (row > boardData.shadow.row && directorY > 0) {
        var maxHeight = 0
        if (!boards[row]) {
          boards.push([])
        }
        for (var i = 0; i < boardData.shadow.width; i++) {
          var name = boards[row + boardData.shadow.height - 1][col + i]
          if (name && name !== 'shadow') {
            maxHeight = maxHeight >= boardData[name].height ? maxHeight : boardData[name].height
          }
        }
        row += maxHeight + boardData.shadow.height - 1
      }
      var offset = this.grid2position(row, col)
      $('.container ul li.shadow').css('transform', 'translate(' + (offset.left) + 'px,' + (offset.top) + 'px)')
      return {'row': row, 'col': col}
    },

    createBoardsData () {
      this.initBoardItem()
      this.reflectData()
    },
    initBoardItem () {
      var boardData = this.boardData
      this.boards = []
      var boards = this.boards
      for (var key in boardData) {
        var item = boardData[key]
        this.maxRow = this.maxRow < item.row + item.height ? item.row + item.height : this.maxRow
        this.maxCol = this.maxCol < item.col + item.width ? item.col + item.width : this.maxCol
      }
      for (var i = 0; i < this.maxRow; i++) {
        if (!boards[i]) {
          boards.push([])
        }
        for (var j = 0; j < this.maxCol; j++) {
          if (!boards[i][j]) {
            boards[i].push(0)
          }
        }
      }
    },
    reflectData () {
      var boards = this.boards
      var boardData = this.boardData
      for (var key in boardData) {
        var item = boardData[key]
        var width = item.width
        var height = item.height
        var row = item.row
        var col = item.col
        var name = item.name
        for (var i = 0; i < height; i++) {
          for (var j = 0; j < width; j++) {
            boards[row + i][col + j] = name
          }
        }
      }
    },
    removeElement (name) {
      delete this.boardData[name]
      this.createBoardsData()
    },
    addElement (item) {
      this.boardData[item.name] = item
      this.createBoardsData()
    },
    locate () {
      var boardData = this.boardData
      for (var key in boardData) {
        var item = boardData[key]
        var jdom = $('[item-name="' + key + '"]')
        var size = this.grid2size(item.width, item.height)
        var offset = this.grid2position(item.row, item.col)
        jdom.width(size.width).height(size.height).css('transform', 'translate(' + offset.left + 'px,' + offset.top + 'px)')
        jdom.show()
      }
    },
    grid2size (width, height) {
      return {'width': width * this.cell + (width - 1) * this.space, 'height': height * this.cell + (height - 1) * this.space}
    },
    grid2position (row, col) {
      var left = col * this.cell + col * this.space
      var top = row * this.cell + row * this.space
      return {'left': left, 'top': top}
    },
    copyGrid (grid) {
      var obj = {}
      obj.name = grid.name
      obj.row = grid.row
      obj.col = grid.col
      obj.width = grid.width
      obj.height = grid.height
      return obj
    },
    position2grid (x, y) {
      return {
        'row': parseInt(y / (this.space + this.cell)),
        'col': parseInt(x / (this.space + this.cell))
      }
    },
    locateGrid (top, left, offsetX, offsetY) {
      var row = 0
      var col = 0
      if (offsetX >= 0) {
        left += this.cell
        // 根据公式(col+1)*cell+col*space+cell/2+space=left计算而得
        col = parseInt((left - this.cell / 2) / (this.space + this.cell))
      } else {
        // 根据公式(col-1)*(space+cell)+cell/2=left计算而得
        col = left < this.cell / 2 ? 0 : (parseInt((left - this.cell / 2) / (this.space + this.cell)) + 1)
      }
      if (offsetY >= 0) {
        top += this.cell
        row = parseInt((top - this.cell / 2) / (this.space + this.cell))
      } else {
        row = top < this.cell / 2 ? 0 : (parseInt((top - this.cell / 2) / (this.space + this.cell)) + 1)
      }
      return {'row': row < 0 ? 0 : row, 'col': col < 0 ? 0 : col}
    },
    moveTo (item, row, col) {
      var stack = []
      var boards = this.boards
      // 压栈操作
      for (var i = 0; i < item.height; i++) {
        for (var j = 0; j < item.width; j++) {
          // 如果到了数组边界,直接跳过表示可以移动,且没其它元素
          if (!boards[row + i]) {
            continue
          }
          var grid = boards[row + i][col + j]
          if (grid && grid !== item.name && grid !== 'shadow') {
            if (!this.hasGrid(stack, grid)) {
              stack.push(this.boardData[grid])
            }
          }
        }
      }
      // 栈里面的元素继续移动
      if (stack.length > 0) {
        for (var k = stack.length - 1; k >= 0; k--) {
          // 第二个参数row+item.height非常重要，下移到目标位置行 为拖动的元素块的高度+拖动到的行高
          if (item.name === 'shadow') {
            for (var l = 1; l <= item.height; l++) {
              this.moveTo(stack[k], row + l, stack[k].col)
            }
          } else {
            this.moveTo(stack[k], row + item.height, stack[k].col)
          }
        }
      }
      // 将元素移动到目标位置
      item.row = row
      item.col = col
      this.createBoardsData()
    },
    hasGrid (arr, grid) {
      for (var i = 0; i < arr.length; i++) {
        if (arr[i].name === grid.name) {
          return true
        }
      }
      return false
    },
    adjustLocation () {
      var adjustFlag = false
      var boardData = this.boardData
      this.boards.forEach(function (row, i) {
        row.forEach(function (name, j) {
          if (!name) {
            return
          }
          var item = boardData[name]
          if (this.canMove(item)) {
            item.row = item.row - 1
            adjustFlag = true
          }
          // TODO 下面算法需要优化，不能每移一个元素就重新生成所有数据
          this.createBoardsData()
          this.locate()
        }, this)
      }, this)
      if (adjustFlag) {
        this.adjustLocation()
      }
    },
    canMove (item) {
      var boards = this.boards
      if (!item || item.row === 0) {
        return false
      }
      for (var j = 0; j < item.width; j++) {
        if (boards[item.row - 1][item.col + j]) {
          return false
        }
      }
      return true
    },

    registeEvent () {
      var self = this
      $(document).on('mousedown', '.container li.item' , this.onItemMouseDown)
      $(document).on('mousemove', this.onMouseMove)
      $(document).on('mouseup', this.onMouseUp)
      $(document).on('mousedown', 'li.item .resizeHandle', this.onMouseDownResize)
      $('.item .close').on('click', this.onClickClose)
    },

    onClickClose (e) {
      var self = this
      var jdom = $(e.target).parents('li.item')
      var name = jdom.attr('item-name')
      // 删除dom
      jdom.remove()
      // 删除数据
      delete self.boardData[name]
      // 重构数据
      self.createBoardsData()
      self.locate()
      setTimeout(self.adjustLocation, 200)
      // 发送数据更改事件
      self.$emit('boardDataChange', self.boardData)
    },

    onItemMouseDown (e) {
      var self = this
      self.dragFlag = true
      self.startX = e.pageX
      self.startY = e.pageY
      if (!self.dragDom) {
        self.dragDom = e.target.className.indexOf('item') >= 0 ? e.target : $(e.target).parents('li.item')[0]
      }
      $(self.dragDom).removeClass('animation')
      $(self.dragDom).addClass('changing')
      if (self.dragShadow) {
        self.dragData = self.boardData[$(self.dragDom).attr('item-name')]
        self.createShadow()
      }
      // 防止意外鼠标事件导致dargData不存在时页面拖动出现问题
      if (!self.dragData) {
        return
      }
      self.currentGrid.row = self.dragData.row
      self.currentGrid.col = self.dragData.col
    },

    onMouseDownResize (e) {
      var self = this
      e.stopPropagation()
      self.resizeFlag = true
      self.startX = e.pageX
      self.startY = e.pageY
      if (!self.dragDom) {
        self.dragDom = $(e.target).parent('.item')[0]
      }
      $(self.dragDom).removeClass('animation')
      $(self.dragDom).addClass('changing')
      self.resizeData = self.boardData[$(self.dragDom).attr('item-name')]
      self.removeElement(self.resizeData.name)
      if (self.dragShadow) {
        self.createShadow()
      }
      self.currentGrid.width = self.resizeData.width
      self.currentGrid.height = self.resizeData.height
    },

    onMouseMove (e) {
      var self = this
      if (e.pageX === self.startX && e.pageY === self.startY) {
        return
      }
      var offsetX = 0
      var offsetY = 0
      var offset = 0
      if (self.dragFlag) {
        offsetX = e.pageX - self.startX
        offsetY = e.pageY - self.startY
        offset = $(self.dragDom).position()
        $(self.dragDom).css('transform', 'translate(' + (offset.left + offsetX) + 'px,' + (offset.top + offsetY) + 'px)')
        self.startX = e.pageX
        self.startY = e.pageY
        // 滚动条定位
        // $('body').scrollLeft($('body')[0].scrollWidth);
        // $('body').scrollTop($('body')[0].scrollHeight);
        // 碰撞检测部分
        var row = self.currentGrid.row
        var col = self.currentGrid.col
        var grid = self.locateGrid(offset.top, offset.left, offsetX, offsetY)
        var nowRow = grid.row
        var nowCol = grid.col
        // 若移动了到另一个格子
        if (row !== nowRow || col !== nowCol) {
          // 1.将拖动元素从格子中移除
          self.removeElement(self.dragData.name)
          // 2.移动背影
          var pos = self.moveShadow(nowRow, nowCol, offsetY)
          self.moveTo(self.boardData.shadow, pos.row, pos.col)
          // 3.调整位置
          self.createBoardsData()
          self.adjustLocation()
          self.currentGrid.row = nowRow
          self.currentGrid.col = nowCol
        }
      }
      if (self.resizeFlag) {
        offsetX = e.pageX - self.startX
        offsetY = e.pageY - self.startY
        $(self.dragDom).width($(self.dragDom).width() + offsetX)
        $(self.dragDom).height($(self.dragDom).height() + offsetY)
        offset = self.grid2size(self.resizeData.width, self.resizeData.height)
        if ($(self.dragDom).width() - self.space >= offset.width) {
          self.resizeData.width += 1
        }
        if ($(self.dragDom).height() - self.space >= offset.height) {
          self.resizeData.height += 1
        }
        if ($(self.dragDom).width() < offset.width - self.cell) {
          self.resizeData.width -= 1
        }
        if ($(self.dragDom).height() < offset.height - self.cell) {
          self.resizeData.height -= 1
        }
        if (self.currentGrid.width !== self.resizeData.width || self.currentGrid.height !== self.resizeData.height) {
          offset = self.grid2size(self.resizeData.width, self.resizeData.height)
          $('.container ul li.shadow').width(offset.width).height(offset.height)
          self.adjustLocation()
          self.moveTo(self.resizeData, self.resizeData.row, self.resizeData.col)
          self.locate()
        }
        self.currentGrid.width = self.resizeData.width
        self.currentGrid.height = self.resizeData.height
      }
      self.startX = e.pageX
      self.startY = e.pageY
    },

    onMouseUp (e) {
      var self = this
      $(self.dragDom).addClass('animation')
      $(self.dragDom).removeClass('changing')
      // 恢复操作
      if (self.dragData) {
        self.dragData.row = self.boardData.shadow.row
        self.dragData.col = self.boardData.shadow.col
        self.addElement(self.dragData)
        // self.adjustLocation()
        self.$emit('boardDataChange', self.boardData)
      }
      if ($('.container ul li.shadow')[0]) {
        self.removeShadow()
      }
      if (self.resizeData) {
        var offset = self.grid2size(self.resizeData.width, self.resizeData.height)
        $(self.dragDom).width(offset.width).height(offset.height)
        self.boardData[self.resizeData.name] = self.resizeData
        self.createBoardsData()
        self.$emit('boardDataChange', self.boardData)
        // self.adjustLocation()
        // 触发事件 TODO 这里需要优化，不能通过这种方式来进行图表适配
        setTimeout(function(){
          $(window).resize()
        }, 200)
        
      }
      self.dragFlag = false
      self.resizeFlag = false
      self.startX = 0
      self.startY = 0
      self.dragDom = null
      self.dragData = null
      self.shadowOffset = null
      self.resizeData = null
      self.currentGrid = {}
    }
  },
  created () {
    var self = this
    console.log('dashboard created')
    $('body').css('user-select', 'none')
    this.createBoardsData()
    this.locate()
    setTimeout(function(){
      self.adjustLocation()
    }, 100)
    setTimeout(function(){
      self.registeEvent()
    }, 1000)
  }
}
</script>
